﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using OpenQuarters.WebQuarters.Core;
using OpenQuarters.WebQuarters.Utils;
using System.IO;
using System.Collections;

namespace OpenQuarters.WebQuarters.Modules.Core
{
    public class NavigationModule : Module
    {
        public static Dictionary<string, string> PageHierarchy
        {
            get
            {
                Dictionary<string, string> pages = new Dictionary<string, string>();
                pages.Add("-1", "[Level 1]");
                pages.Add("-2", "[Level 2]");
                pages.Add("-3", "[Level 3]");
                pages.Add("-4", "[Level 4]");
                pages.Add("-5", "[Level 5]");
                pages.Add("-10", "[Parent]");
                pages.Add("0", "[Current]");

                StringBuilder sb = new StringBuilder();
                getChildPages(pages, CMSPage.Pages, 0);

                return pages;
            }
        }

        private static void getChildPages(Dictionary<string, string> list, CMSPageCollection pages, int level)
        {
            int index = 0;
            if (pages.Count > 0)
            {
                foreach (var p in pages)
                {
                    if (p.Title.Length > 0)
                    {
                        list.Add(p.PageId.ToString(), p.Title.PadLeft(p.Title.Length + level, '>'));
                        getChildPages(list, p.json_ChildPages, level + 1);
                    }
                }
            }
        }

        private long start_Page = CMSPage.HomePage.PageId;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.Core.NavigationModule.PageHierarchy")]
        public long Start_Page
        {
            get { return start_Page; }
            set { start_Page = value; }
        }

        private long FindSelectedLevel(int level)
        {
            CMSPage page = CMSPage.Current.CMSPage;
            List<CMSPage> pageLevels = new List<CMSPage>();

            pageLevels.Add(page);
            while (page.ParentId.HasValue)
            {
                page = CMSPage.Load(page.ParentId.Value);
                pageLevels.Add(page);
            }

            if (pageLevels.Count != 0 && pageLevels.Count >= level)
            {
                return pageLevels[pageLevels.Count - level].PageId;
            }
            else
                return page.PageId;
        }

        private bool start_Page_at_2nd_Level = false;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CheckBox)]
        public bool Start_Page_at_2nd_Level
        {
            get { return start_Page_at_2nd_Level; }
            set { start_Page_at_2nd_Level = value; }
        }

        private bool flatten_Hierarchy = false;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CheckBox)]
        public bool Flatten_Hierarchy
        {
            get { return flatten_Hierarchy; }
            set { flatten_Hierarchy = value; }
        }

        private string include_Tags = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public string Include_Tags
        {
            get { return include_Tags; }
            set { include_Tags = value.Replace(", ", ",").Replace(" ,", "").Replace("  ", " "); }
        }

        private string exclude_Tags = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public string Exclude_Tags
        {
            get { return exclude_Tags; }
            set { exclude_Tags = value.Replace(", ", ",").Replace(" ,", "").Replace("  ", " "); }
        }

        private int maximumLevels = 1;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public int Maximum_levels
        {
            get { return maximumLevels; }
            set { maximumLevels = value; }
        }

        private int maximumRootElements = 0;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public int Maximum_Root_Elements
        {
            get { return maximumRootElements; }
            set { maximumRootElements = value; }
        }

        private bool breadcrumbOnly = false;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CheckBox)]
        public bool Breadcrumb_Only
        {
            get { return breadcrumbOnly; }
            set { breadcrumbOnly = value; }
        }

        public string[] IncludedTags()
        {
            if (string.IsNullOrEmpty(this.include_Tags))
                return new string[] { };
            else
                return this.include_Tags.Split(',');
        }

        public string[] ExcludedTags()
        {
            if (string.IsNullOrEmpty(this.exclude_Tags))
                return new string[] { };
            else
                return this.exclude_Tags.Split(',');
        }

        public static Dictionary<string, string> Templates
        {
            get
            {
                var d = new Dictionary<string, string>();
                d.Add("", "[Default]");
                foreach (var dir in new DirectoryInfo(Oracle.Context.Server.MapPath("~/Templates/")).GetDirectories())
                {
                    if (dir.Name.Contains(typeof(NavigationModule).FullName))
                    {
                        foreach (var template in dir.GetFiles("*.ascx", SearchOption.TopDirectoryOnly))
                        {
                            d.Add(template.Name.Replace(".ascx", ""), template.Name.Replace(".ascx", ""));
                        }
                    }
                }
                return d;
            }
        }

        private string template = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.Core.NavigationModule.Templates")]
        public string Template
        {
            get { return template; }
            set { template = value; }
        }

        private bool enableCaching = true;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CheckBox)]
        public bool Enable_Caching
        {
            get { return enableCaching; }
            set { enableCaching = value; }
        }

        private List<long> breadcrumbIds;

        public NavigationModule()
        {
        }

        public override void Initialise()
        {
            this.ModuleName = "Navigation Module";
            this.ModuleDescription = "Allows a site map or navigation menu to be added to the page";
        }

        public override string GetJQueryEditorName()
        {
            return "cmsModuleEditor, cmsModuleEditor_Navigation";
        }

        [Serializable]
        struct HierarchyPage
        {
            public CMSPage page;
            public List<HierarchyPage> children;
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            JavaScript.RegisterScript("Modules.Navigation.AdminScript.js", "OpenQuarters.WebQuarters.Modules.Core", "Resources.Navigation.AdminScript.js", JavaScriptType.AdminScript);
            breadcrumbIds = GetBreadcrumbIds(CMSPage.Current.CMSPage);

            long startPageId = start_Page;

            if (start_Page <= 0)
            {
                switch (start_Page)
                {
                    case -10:
                        long? parentId = CMSPage.Current.CMSPage.ParentId;
                        startPageId = (parentId.HasValue ? parentId.Value : start_Page);
                        break;
                    case -1:
                    case -2:
                    case -3:
                    case -4:
                    case -5:
                        startPageId = FindSelectedLevel(Convert.ToInt32(start_Page * -1));
                        break;
                    case 0:
                        startPageId = CMSPage.Current.PageId;
                        break;
                    default:
                        startPageId = start_Page;
                        break;
                }
            }

            StringBuilder sb = new StringBuilder();
            List<HierarchyPage> hierarchy = null;

#if !DEBUG
            if (enableCaching
                && !OpenQuarters.WebQuarters.Core.User.Current.HasPermission(OpenQuarters.WebQuarters.Core.Permissions.CMSPage.Edit.SetValue( CMSPage.Current.PageId.ToString()))
                && ((start_Page > 0 && Session["cmsNavigationModule"] != null && ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"]).ContainsKey(ModuleId)) || (start_Page < 1 && Session["cmsNavigationModule"] != null && ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"]).ContainsKey(ModuleId + "_" + CMSPage.Current.PageId))))
            {
                if (start_Page < 1)
                {
                    hierarchy = ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"])[this.ModuleId + "_" + CMSPage.Current.PageId];
                } 
                else 
                {
                    hierarchy = ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"])[this.ModuleId];
                }
            }
#endif


            if (hierarchy == null)
            {
                if (flatten_Hierarchy)
                {
                    hierarchy = new List<HierarchyPage>();

                    flattenHierarchy(hierarchy, CMSPage.Load(startPageId), 1);
                }
                else
                {
                    hierarchy = new List<HierarchyPage>();

                    recurseHierarchy(hierarchy, CMSPage.Load(startPageId), 1);
                }

                if (start_Page < 1)
                {
                    // Cache nav per page
                    if (Session["cmsNavigationModule"] == null)
                    {
                        var sess = new Dictionary<string, List<HierarchyPage>>();
                        sess[this.ModuleId + "_" + CMSPage.Current.PageId] = hierarchy;
                        Session["cmsNavigationModule"] = sess;
                    }
                    else
                    {
                        ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"])[this.ModuleId + "_" + CMSPage.Current.PageId] = hierarchy;
                    }
                }
                else
                {
                    if (Session["cmsNavigationModule"] == null)
                    {
                        var sess = new Dictionary<string, List<HierarchyPage>>();
                        sess[this.ModuleId] = hierarchy;
                        Session["cmsNavigationModule"] = sess;
                    }
                    else
                    {
                        ((Dictionary<string, List<HierarchyPage>>)Session["cmsNavigationModule"])[this.ModuleId] = hierarchy;
                    }
                }
            }
            recurseAddPages(sb, hierarchy, 1);
            this.Controls.AddLiteral(sb.ToString());
        }

        public List<long> GetBreadcrumbIds(CMSPage currentPage)
        {
            List<long> breadcrumbIds = new List<long>();

            while (currentPage != null)
            {
                breadcrumbIds.Add(currentPage.PageId);
                currentPage = currentPage.ParentPage;
            }

            return breadcrumbIds;
        }

        private void recurseHierarchy(List<HierarchyPage> hierarchy, CMSPage page, int level)
        {
            int index = 0;
            List<CMSPage> pages = page.ChildPages.OrderBy(p => p.Order).ToList();

            if (level == 1 && start_Page_at_2nd_Level && CheckMatches(page))
            {
                HierarchyPage hierarchyPage = new HierarchyPage();
                hierarchyPage.page = page;
                hierarchyPage.children = new List<HierarchyPage>();
                hierarchy.Add(hierarchyPage);
                index++;
            }


            if (pages.Count > 0 && level <= Maximum_levels)
            {
                foreach (var p in pages)
                {
                    bool matchesTags = CheckMatches(p);

                    if (((level != 1 || (level == 1 && (maximumRootElements == 0 || index + 1 <= maximumRootElements))) && p.Title.Length > 0) && matchesTags && (breadcrumbOnly ? breadcrumbIds.Contains(p.PageId): true))
                    {
                        HierarchyPage hierarchyPage = new HierarchyPage();
                        hierarchyPage.page = p;
                        hierarchyPage.children = new List<HierarchyPage>();
                        recurseHierarchy(hierarchyPage.children, p, level + 1);

                        if (level + 1 <= Maximum_levels)
                        {
                            foreach (ContentAreaDefinition area in p.CurrentVersion.ContentDefinition.Areas)
                            {
                                foreach (ModuleDefinition module in area.Modules)
                                {
                                    if (GeneralUtils.GetTypeByName(module.ModuleType) != null && GeneralUtils.GetTypeByName(module.ModuleType.ToString()).GetInterfaces().FirstOrDefault(i => i.Name == "IItemListModule`1") != null)
                                    {

                                        object moduleObj = null;
                                        Type moduleType = GeneralUtils.GetTypeByName(module.ModuleType);
                                        if (moduleType != null)
                                        {
                                            try
                                            {
                                                moduleObj = Activator.CreateInstance(moduleType);
                                                typeof(GeneralUtils).GetMethod("FromJSON", new Type[] { typeof(string), typeof(object) }).MakeGenericMethod(moduleType).Invoke(null, new object[] { module.ModuleJSON, moduleObj });

                                                IList myList = (IList)moduleObj.GetType().GetMethod("ChildItems").Invoke(moduleObj, new object[] { });

                                                foreach (IItemListable item in myList)
                                                {
                                                    HierarchyPage childPage = new HierarchyPage();
                                                    childPage.children = new List<HierarchyPage>();
                                                    childPage.page = new CMSPage();
                                                    childPage.page.ParentId = p.PageId;
                                                    childPage.page.EnsureMockVersion(item.ItemTitle());
                                                    childPage.page.MockVersion.Keywords = item.MetaKeywords();
                                                    childPage.page.MockVersion.Description = item.MetaDescription();
                                                    Url url = new Url();
                                                    url.PageUrl = p.Urls[0] + "/" + item.ItemUrl();
                                                    childPage.page.Urls.Add(url);

                                                    hierarchyPage.children.Add(childPage);
                                                }
                                            }
                                            catch (NotImplementedException)
                                            {
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        hierarchy.Add(hierarchyPage);
                    }
                    index++;
                }
            }
        }

        private void flattenHierarchy(List<HierarchyPage> hierarchy, CMSPage page, int level)
        {
            int index = 0;
            List<CMSPage> pages = page.ChildPages.OrderBy(p => p.Order).ToList();

            if (level == 1 && start_Page_at_2nd_Level && CheckMatches(page))
            {
                HierarchyPage hierarchyPage = new HierarchyPage();
                hierarchyPage.page = page;
                hierarchyPage.children = new List<HierarchyPage>();
                hierarchy.Add(hierarchyPage);
                index++;
            }

            if (pages.Count > 0 && level <= Maximum_levels)
            {
                foreach (var p in pages)
                {
                    bool matchesTags = CheckMatches(p);

                    if (((level != 1 || (level == 1 && (maximumRootElements == 0 || index + 1 <= maximumRootElements))) && p.Title.Length > 0) && matchesTags && (breadcrumbOnly ? breadcrumbIds.Contains(p.PageId) : true))
                    {
                        HierarchyPage hierarchyPage = new HierarchyPage();
                        hierarchyPage.page = p;
                        hierarchyPage.children = new List<HierarchyPage>();
                        hierarchy.Add(hierarchyPage);
                        flattenHierarchy(hierarchy, p, level + 1);
                        
                    }
                    index++;
                }
            }
        }

        private bool CheckMatches(CMSPage p)
        {
            bool matchesTags = true;

            if (!string.IsNullOrEmpty(this.include_Tags))
            {
                foreach (string tag in this.IncludedTags())
                {
                    if (p.CurrentVersion.GetTags().Contains(tag))
                    {
                        matchesTags = true;
                        break;
                    }
                    matchesTags = false;
                }
            }

            if (matchesTags && !string.IsNullOrEmpty(this.exclude_Tags))
            {
                foreach (string tag in this.ExcludedTags())
                {
                    if (p.CurrentVersion != null && p.CurrentVersion.GetTags().Contains(tag))
                        matchesTags = false;
                }
            }

            return matchesTags;
        }

        private void recurseAddPages(StringBuilder sb, List<HierarchyPage> hierarchy, int level)
        {
            int index = 0;

            List<HierarchyPage> pages = hierarchy.OrderBy(h => h.page.Order).ToList();
            StringBuilder sbSubPages = new StringBuilder();

            foreach (var hierarchyPage in pages)
            {

                var p = hierarchyPage.page;

                string cssClass = "id" + p.PageId + " level" + level.ToString();
                if (index == 0)
                    cssClass = cssClass + " first";
                if (index == pages.Count - 1 || (maximumRootElements != 0 && index + 1 == maximumRootElements))
                    cssClass = cssClass + " last";
                if (p.ParentId != 0)
                    cssClass = cssClass + " child";
                if (p.ChildPages.Count != 0)
                    cssClass = cssClass + " parent";
                if (p.PageId == CMSPage.Current.PageId)
                    cssClass = cssClass + " selected";
                if (breadcrumbIds.Contains(p.PageId))
                    cssClass = cssClass + " breadcrumb";

                if (p.Title.Length > 0)
                {
                    if (User.Current.HasPermission(Permissions.CMSPage.Edit.SetValue(CMSPage.Current.PageId.ToString())))
                        sbSubPages.Append("<li class=\"" + cssClass + "\" rel=\"" + p.PageId.ToString() + "\">");
                    else
                        sbSubPages.Append("<li class=\"" + cssClass + "\">");

                    if (!string.IsNullOrEmpty(this.template))
                    {
                        var viewPath = "~/Templates/" + this.GetType().FullName + "/" + this.Template + ".ascx";
                        //sbSubPages.Append(GeneralUtils.RenderViewToString(
                        //        Oracle.CurrentControllerContext,
                        //        viewPath,
                        //        null,
                        //        new ViewDataDictionary(p),
                        //        new TempDataDictionary()));
                        sbSubPages.Append(GeneralUtils.RenderPartialToString(viewPath, p));
                    }
                    else
                    {
                        sbSubPages.Append(p.GetLinkHTML());
                    }

                    if (hierarchyPage.children.Count() != 0)
                        recurseAddPages(sbSubPages, hierarchyPage.children, level + 1);

                    sbSubPages.Append("</li>");
                }

                index++;
            }

            if (sbSubPages.Length > 0)
            {
                sb.Append("<ul>" + sbSubPages.ToString() + "</ul>");
            }
        }

        //private void recursedFlattedHierarchy(StringBuilder sb, CMSPage page, int index, int level, bool includeRoot)
        //{
        //    //List<CMSPage> pages = CMSPage.Table.Where(p => p.ParentId == page.PageId).ToList().OrderBy(p => p.Order).ToList();
        //    List<CMSPage> pages = page.ChildPages.OrderBy(p => p.Order).ToList();

        //    if (includeRoot)
        //    {
        //        pages.Insert(0, page);
        //    }

        //    if (pages.Count > 0 && level <= Maximum_levels)
        //    {
        //        StringBuilder sbSubPages = new StringBuilder();
        //        foreach (var p in pages)
        //        {
        //            if ((level != 1 || (level == 1 && (maximumRootElements == 0 || index + 1 <= maximumRootElements))) && p.Title.Length > 0)
        //            {
        //                bool matchesTags = true;

        //                if (!string.IsNullOrEmpty(this.include_Tags))
        //                {
        //                    foreach (string tag in this.IncludedTags())
        //                    {
        //                        if (p.CurrentVersion.GetTags().Contains(tag))
        //                        {
        //                            matchesTags = true;
        //                            break;
        //                        }
        //                        matchesTags = false;
        //                    }
        //                }

        //                if (matchesTags && !string.IsNullOrEmpty(this.exclude_Tags))
        //                {
        //                    foreach (string tag in this.ExcludedTags())
        //                    {
        //                        if (p.CurrentVersion != null && p.CurrentVersion.GetTags().Contains(tag))
        //                            matchesTags = false;
        //                    }
        //                }

        //                if (matchesTags)
        //                {
        //                    string cssClass = "level" + level.ToString();
        //                    if (index == 0)
        //                        cssClass = cssClass + " first";
        //                    if (index == pages.Count - 1 || (maximumRootElements != 0 && index + 1 == maximumRootElements))
        //                        cssClass = cssClass + " last";
        //                    if (p.ParentId != 0)
        //                        cssClass = cssClass + " child";
        //                    if (p.ChildPages.Count != 0)
        //                        cssClass = cssClass + " parent";
        //                    if (p.PageId == CMSPage.Current.PageId)
        //                        cssClass = cssClass + " selected";
        //                    if (breadcrumbIds.Contains(p.PageId))
        //                        cssClass = cssClass + " breadcrumb";

        //                    if (p.Title.Length > 0)
        //                    {
        //                        sbSubPages.Append("<li class=\"" + cssClass + "\" rel=\"" + p.PageId.ToString() + "\">");

        //                        if (!string.IsNullOrEmpty(this.template))
        //                        {
        //                            var viewPath = "~/Templates/" + this.GetType().FullName + "/" + this.Template + ".ascx";
        //                            sbSubPages.Append(GeneralUtils.RenderViewToString(
        //                                    Oracle.CurrentControllerContext,
        //                                    viewPath,
        //                                    null,
        //                                    new ViewDataDictionary(p),
        //                                    new TempDataDictionary()));
        //                        }
        //                        else
        //                        {
        //                            sbSubPages.Append(p.GetLinkHTML());
        //                        }

        //                        sbSubPages.Append("</li>");

        //                        if (!(start_Page_at_2nd_Level && p.PageId == Start_Page))
        //                            recursedFlattedHierarchy(sbSubPages, p, index, level + 1, false);
        //                    }
        //                }
        //                else
        //                    recursedFlattedHierarchy(sbSubPages, p, index, level + 1, false);
        //            }
        //            index++;
        //        }
        //        if (sbSubPages.Length > 0)
        //        {
        //            sb.Append(sbSubPages.ToString());
        //        }
        //    }
        //}
        //}
    }
}
